Part 8 - Plan-এর সাথে পরিচয় (Introduction to Plans)

প্রসঙ্গ (Context)

আমরা এখানে এমন একটি বিষয় প্রবর্তন করি যা শিল্প ফেডারেটড লার্নিং স্কেল করার জন্য অত্যন্ত গুরুত্বপূর্ণ, সেটি হলো Plan। এটি নাটকীয়ভাবে ব্যান্ডউইথের ব্যবহার হ্রাস করে, অ্যাসিনক্রোনাস স্কিমগুলির অনুমতি দেয় এবং দূরবর্তী ডিভাইসগুলিতে আরও স্বায়ত্তশাসন দেয়। পরিকল্পনার মূল ধারণাটি কাগজে পাওয়া যাবে Towards Federated Learning at Scale: System Design, তবে এটি পাইসাইফ্ট লাইব্রেরিতে আমাদের প্রয়োজনের সাথে খাপ খাইয়ে নিয়েছে।

একটি পরিকল্পনা, ঠিক একটি ফাংশনের মতো torch ক্রিয়াকলাপগুলির ক্রম সংরক্ষণ করার উদ্দেশ্যে তৈরি করা হয়েছিল, তবে এটি অপারেশনগুলির এই ক্রমটি প্রত্যন্ত কর্মীদের কাছে প্রেরণ এবং এটির একটি রেফারেন্স রাখতে দেয়। এইভাবে, দূরবর্তী অবস্থান থেকে এই ক্রম গণনা করা $n$ পাঠানোর পরিবর্তে পয়েন্টারগুলির মাধ্যমে কিছু দূরবর্তী ইনপুটটিতে ক্রিয়াকলাপ $n$ পরিকল্পনার উল্লেখ এবং পয়েন্টারগুলির সাথে একক বার্তা প্রেরণের জন্য আপনার এখন যে বার্তাগুলি প্রয়োজন সেগুলি। আপনি আপনার ফাংশন সহ টেনারগুলিও সরবরাহ করতে পারেন (যা আমরা কল করি state tensors ) বর্ধিত কার্যকারিতা আছে। পরিকল্পনাগুলি আপনি যে কোনও ফাংশন প্রেরণ করতে পারেন তার মতো দেখা যায়, বা এমন কোনও শ্রেণীর মতো যা প্রেরণ এবং দূরবর্তীভাবে কার্যকর করা যায়। অতএব, উচ্চ স্তরের ব্যবহারকারীদের জন্য, পরিকল্পনার ধারণাটি অদৃশ্য হয়ে যায় এবং এর পরিবর্তে একটি যাদু বৈশিষ্ট্য রয়েছে যা দূরবর্তী শ্রমিকদের সিক্যুয়ালি টর্চ ফাংশনগুলি সহ নির্বিচারে ফাংশনগুলি প্রেরণ করতে দেয়।

একটি বিষয় লক্ষ্য করুন যে ফাংশনগুলির শ্রেণি যা আপনি পরিকল্পনাগুলিতে রূপান্তর করতে পারেন তা কেবল একচেটিয়াভাবে টুকরা টর্চ ক্রিয়াকলাপগুলির মধ্যে সীমাবদ্ধ। এটি যেমন নির্দিষ্ট যৌক্তিক কাঠামো বাদ দেয় if, for এবং while বিবৃতি, এমনকি যদি আমরা শীঘ্রই প্রায় কাজ করতে কাজ করছি। সম্পূর্ণরূপে সুনির্দিষ্ট হওয়ার জন্য, আপনি এগুলি ব্যবহার করতে পারেন তবে আপনার নেওয়া যৌক্তিক পথটি (প্রথমে if মিথ্যা এবং 5 লুপ ইন for উদাহরণ স্বরূপ) আপনার পরিকল্পনার প্রথম গণিতে পরবর্তী সমস্ত গণনাগুলির জন্য রাখা হবে, যা আমরা বেশিরভাগ ক্ষেত্রে এড়াতে চাই।

Authors:

অনুবাদক:

আমদানি এবং মডেল বিশেষ উল্লেখ (Imports and model specifications)

প্রথমে অফিসিয়ালগুলো আমদানি করি।


In [0]:
import torch
import torch.nn as nn
import torch.nn.functional as F

এবং এরপর পাইসাইফ্ট সম্পর্কিত নির্দিষ্টগুলি আমদানি করি একটি গুরুত্বপূর্ণ নোট সহ: স্থানীয় কর্মী ক্লায়েন্ট কর্মী হওয়া উচিত নয়(the local worker should not be a client worker.) নন ক্লায়েন্ট কর্মীরা বস্তু সংরক্ষণ করতে পারে এবং একটি Plan চালানোর জন্য আমাদের এই দক্ষতার প্রয়োজন.


In [0]:
import syft as sy  # import the Pysyft library
hook = sy.TorchHook(torch)  # hook PyTorch ie add extra functionalities 

# IMPORTANT: Local worker should not be a client worker
hook.local_worker.is_client_worker = False


server = hook.local_worker

আমরা দূরবর্তী কর্মীদের বা ডিভাইসগুলোকে ডিফাইন করি , রেফারেন্স নিবন্ধে সরবরাহিত ধারণার সাথে সামঞ্জস্যপূর্ণ থাকতে। আমরা তাদের কিছু তথ্য সরবরাহ করি।


In [0]:
x11 = torch.tensor([-1, 2.]).tag('input_data')
x12 = torch.tensor([1, -2.]).tag('input_data2')
x21 = torch.tensor([-1, 2.]).tag('input_data')
x22 = torch.tensor([1, -2.]).tag('input_data2')

device_1 = sy.VirtualWorker(hook, id="device_1", data=(x11, x12)) 
device_2 = sy.VirtualWorker(hook, id="device_2", data=(x21, x22))
devices = device_1, device_2

বেসিক উদাহরণ (Basic example)

আসুন একটি ফাংশন ডিফাইন করি যা আমরা একটি Plan-এ রূপান্তর করতে চাই। এটি করার জন্য, এটি ফাংশন ডেফিনেশনের উপরে একটি ডেকরেটর যোগ করার মত সহজ!


In [0]:
@sy.func2plan()
def plan_double_abs(x):
    x = x + x
    x = torch.abs(x)
    return x

আসুন পরীক্ষা করে দেখি, হ্যাঁ আমাদের এখন একটি Plan আছে!


In [0]:
plan_double_abs

একটি Plan ব্যবহার করতে আপনার দুটি জিনিস দরকারঃ পরিকল্পনাটি তৈরি করা (উদাহরণস্বরূপঃ আপনি ফাংশনটিতে উপস্থিত ক্রিয়াকলাপের ক্রমটি নিবন্ধ করুন) এবং এটি কোনও কর্মী/ডিভাইসে প্রেরণ করা। ভাগ্যক্রমে আপনি এটি খুব সহজেই করতে পারেন!

একটি Plan তৈরি করা(Building a plan)

একটি পরিকল্পনা তৈরি করতে আপনাকে এটিকে কিছু ডেটাতে কল করতে হবে।

প্রথমে কিছু দূরবর্তী ডেটার জন্য একটি রেফারেন্স পাইঃ নেটওয়ার্কের মাধ্যমে একটি অনুরোধ প্রেরণ করা হয় এবং একটি রেফারেন্স পয়েন্টারটি ফেরত দেওয়া হয়।


In [0]:
pointer_to_data = device_1.search('input_data')[0]
pointer_to_data

আমরা যদি Plan-কে বলি এটি অবশ্যই কার্যকর করা উচিত দূরবর্তীভাবে location:device_1 ডিভাইসে... আমরা সেক্ষেত্রে ত্রুটি পাবো কারণ Plan-টি এখনও নির্মিত হয়নি।


In [0]:
plan_double_abs.is_built

In [0]:
# Sending non-built Plan will fail
try:
    plan_double_abs.send(device_1)
except RuntimeError as error:
    print(error)

একটি Plan তৈরি করতে আপনাকে কেবল Plan এর উপরে build কল করতে হবে এবং পরিকল্পনাটি কার্যকর করতে প্রয়োজনীয় আর্গুমেন্টগুলো পাস করুন (a.k.a কিছু ডেটা)। যখন কোনও Plam তৈরি করা হয় তখন সমস্ত কমান্ড স্থানীয় কর্মী ক্রমান্বয়ে সম্পাদন করে, এবং পরিকল্পনার দ্বারা ক্যাচ করা হয় এবং readable_plan অ্যাট্রিবিউট এর মধ্যে সংরক্ষণ করা হয়!


In [0]:
plan_double_abs.build(torch.tensor([1., -2.]))

In [0]:
plan_double_abs.is_built

আমরা যদি এখন Plan-টি পাঠানোর চেষ্টা করি তবে এটি কাজ করে!


In [0]:
# This cell is executed successfully
pointer_plan = plan_double_abs.send(device_1)
pointer_plan

ততক্ষণে টেনেসরের সাথে আমরা প্রেরিত বস্তুর একটি পয়েন্টার পাই। এখানে একে বলা হয় PointerPlan.

একটি গুরুত্বপূর্ণ বিষয় মনে রাখবেন যে যখন একটি Plan তৈরি করা হয় তখন আমরা গণনার পূর্বে id(s) প্রি-সেট করে থাকি যেখানে result(s) সংরক্ষণ করা উচিত। এটি ইতিমধ্যে ভার্চুয়াল ফলাফলের একটি রেফারেন্স রাখতে এবং দূরবর্তী ফলাফলটি গণনা করার জন্য অপেক্ষা না করে স্থানীয় গণনা চালিয়ে যাওয়ার জন্য, অবিচ্ছিন্নভাবে কমান্ডগুলি প্রেরণের অনুমতি দেবে। একটি গুরুত্বপূর্ণ অ্যাপ্লিকেশন হলো যখন আপনার device_1 -এ একটি ব্যাচের গণনা প্রয়োজন এবং এই গণনাটি শেষের অপেক্ষা না করেই device_2 তে অন্য একটি ব্যাচ চালু করে দিতে পারি।

দূরবর্তীভাবে একটি Plan চালানো (Running a Plan remotely)

আমরা এখন দূরবর্তী অবস্থান থেকে কিছু ডেটার পয়েন্টার দিয়ে Plan দিকে কল করে পরিকল্পনাটি চালাতে পারি। এই পরিকল্পনাটি দূর থেকে চালিত করার জন্য একটি আদেশ জারি করে, যাতে পরিকল্পনার আউটপুটটির পূর্বনির্ধারিত অবস্থানটিতে এখন ফলাফল থাকে (মনে রাখবেন আমরা গণনার আগে ফলাফলের অবস্থান নির্ধারণ করে রেখেছি)। এটির জন্য একটি যোগাযোগের রাউন্ডও প্রয়োজন।

ফলাফলটি কেবল একটি পয়েন্টার, ঠিক যেমন আপনি যখন কোনও সাধারণ হুকড টর্চ ফাংশন কল করেন!


In [0]:
pointer_to_result = pointer_plan(pointer_to_data)
print(pointer_to_result)

এবং আপনি কেবল মানটি আবার জিজ্ঞাসা করতে পারেন।


In [0]:
pointer_to_result.get()

একটি কংক্রিট উদাহরণের দিকে (Towards a concrete example)

কিন্তু আমরা যা করতে চাই তা হল ডিপ এবং ফেডারেটেড লার্নিং-এ Plan প্রয়োগ করা, তাই না? সুতরাং, আপনি যেভাবে নিউরাল নেটওয়ার্ক ব্যবহার করে কাজ করতে ইচ্ছুক, তেমন একটু জটিল একটি উদাহরণ দেখা যাক। খেয়াল করুন যে আমরা এখন একটি ক্লাসকে Plan-এ রূপান্তর করছি। এটি করার জন্য, আমরা আমাদের ক্লাসকে sy.Plan থেকে inherit করবো(nn.Module এর থেকে inherite-এর পরিবর্তে)।


In [0]:
class Net(sy.Plan):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(2, 3)
        self.fc2 = nn.Linear(3, 2)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return F.log_softmax(x, dim=0)

In [0]:
net = Net()

In [0]:
net

আসুন কিছু মক ডেটা ব্যবহার করে Plan-টি তৈরি করি।


In [0]:
net.build(torch.tensor([1., 2.]))

আমরা এখন পরিকল্পনাটি দূরবর্তী শ্রমিকের কাছে প্রেরণ করি


In [0]:
pointer_to_net = net.send(device_1)
pointer_to_net

আসুন কিছু দূরবর্তী ডেটা উদ্ধার করি


In [0]:
pointer_to_data = device_1.search('input_data')[0]

তারপরে, সিনট্যাক্স সাধারণ দূরবর্তী সিকুয়েনশিয়াল এক্সিকিউশনের মতো, ঠিক যেমন স্থানীয়ভাবে এক্সিকিউট হতো। ক্লাসিক রিমোট এক্সিকিউশনের সাথে তুলনা করলে প্রতিটি এক্সিকিউশনে একটি যোগাযোগ রাউন্ড থাকে।


In [0]:
pointer_to_result = pointer_to_net(pointer_to_data)
pointer_to_result

এবং আমরা যথারীতি ফলাফল পাচ্ছি।


In [0]:
pointer_to_result.get()

Et voilà! আমরা দেখেছি কীভাবে স্থানীয় কর্মী (বা সার্ভার) এবং দূরবর্তী ডিভাইসগুলির মধ্যে যোগাযোগ দারুনভাবে হ্রাস করতে পারে!

কর্মীদের মধ্যে স্যুইচ করুন (Switch between workers)

একটি প্রধান বৈশিষ্ট্য যা আমরা রাখতে চাই তা হলো বেশ কয়েকটি শ্রমিকের জন্য একই Plan ব্যবহার করা, আমরা যে ডেটা বিবেচনা করছি তা দূরবর্তী ব্যাচের উপর নির্ভর করে পরিবর্তন করব। বিশেষত, প্রতিবার কর্মী পরিবর্তন করার পরে আমরা Plan-টি পুনর্নির্মাণ করতে চাই না। আসুন দেখি আমরা কিভাবে এটি করি, আমাদের ছোট নেটওয়ার্কের সাথে পূর্ববর্তী উদাহরণটি ব্যবহার করে।


In [0]:
class Net(sy.Plan):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(2, 3)
        self.fc2 = nn.Linear(3, 2)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return F.log_softmax(x, dim=0)

In [0]:
net = Net()

# Build plan
net.build(torch.tensor([1., 2.]))

এখানে আমাদের নেয়া মূল পদক্ষেপগুলো দেয়া হলো


In [0]:
pointer_to_net_1 = net.send(device_1)
pointer_to_data = device_1.search('input_data')[0]
pointer_to_result = pointer_to_net_1(pointer_to_data)
pointer_to_result.get()

এবং প্রকৃতপক্ষে আপনি একই Plan থেকে অন্যান্য PointerPlans তৈরি করতে পারেন, সুতরাং রিমোটলি অন্য ডিভাইসে Plan চালানোর জন্য সিন্ট্যাক্স একই থাকবে।


In [0]:
pointer_to_net_2 = net.send(device_2)
pointer_to_data = device_2.search('input_data')[0]
pointer_to_result = pointer_to_net_2(pointer_to_data)
pointer_to_result.get()

দ্রষ্টব্য: বর্তমানে, Plan ক্লাসের সাথে আপনি কেবল একটি মেথড ব্যবহার করতে পারেন এবং আপনাকে এটির নাম "forward" রাখতে হবে।

স্বয়ংক্রিয়ভাবে Plan তৈরি করা যেগুলো ফাংশন (Automatically building plans that are functions)

ফাংশনের জন্য (@ sy.func2plan) আমরা build কে কল না করেই স্বয়ংক্রিয়ভাবে Plan তৈরি করতে পারি, প্রকৃতপক্ষে Plan সৃষ্টি হওয়ার সময়ই তৈরি হয়ে যায়।

এই কার্যকারিতাটি পেতে হলে, Plan তৈরি করার সময় আমাদের যে একটি জিনিস পরিবর্তন করতে হবে তা হলো args_shape decorator আর্গুমেন্ট যুক্তকরা। যা কি-না প্রতিটা আর্গুমেন্টের শেপ সম্বলিৎ একটা লিস্ট হবে।


In [0]:
@sy.func2plan(args_shape=[(-1, 1)])
def plan_double_abs(x):
    x = x + x
    x = torch.abs(x)
    return x

plan_double_abs.is_built

Plan-টা তৈরির জন্য প্রদত্ত আকারের মক টেন্সরগুলো তৈরি করতে args_shape প্যারামিটারটি অভ্যন্তরীণভাবে ব্যবহৃত হয়।


In [0]:
@sy.func2plan(args_shape=[(1, 2), (-1, 2)])
def plan_sum_abs(x, y):
    s = x + y
    return torch.abs(s)

plan_sum_abs.is_built

আপনি ফাংশনগুলোতে স্টেটের উপাদানগুলি সরবরাহ করতে পারেন!


In [0]:
@sy.func2plan(args_shape=[(1,)], state=(torch.tensor([1]), ))
def plan_abs(x, state):
    bias, = state.read()
    x = x.abs()
    return x + bias

In [0]:
pointer_plan = plan_abs.send(device_1)
x_ptr = torch.tensor([-1, 0]).send(device_1)
p = pointer_plan(x_ptr)
p.get()

এ সম্পর্কে আরও জানার জন্য, টিউটোরিয়াল Part-8 Bis এ জানতে পারবেন আমরা কিভাবে Plans এর সাথে প্রোটোকল ব্যবহার করি।

গিটহাবে পাইসিফ্ট কে স্টার দিন (Star PySyft on GitHub)

আমাদের সম্প্রদায়কে সাহায্য করার সবচেয়ে সহজ উপায় হল রিপোসিটোরি গুলোতে ষ্টার করা এটি আমরা যে অসাধারণ সরঞ্জামগুলি তৈরি করছি তার সচেতনতা বাড়াতে সহায়তা করে।

গিটহাবে আমাদের টিউটোরিয়ালগুলি চয়ন করুন (Pick our tutorials on GitHub!)

ফেডারেটেড এবং প্রাইভেসি-প্রিজারভেভিং লার্নিংয়ের চেহারা কেমন হওয়া উচিত এবং আমরা এটির জন্য ইটগুলি কীভাবে তৈরি করছি সে সম্পর্কে আরও ভাল ধারণা পেতে আমরা সত্যিই দুর্দান্ত টিউটোরিয়াল তৈরি করেছি।

আমাদের স্ল্যাক যোগ দিন (Join our Slack!)

সর্বশেষতম অগ্রগতিতে আপ টু ডেট রাখার সর্বোত্তম উপায় হ'ল আমাদের সম্প্রদায়ে যোগদান করা! আপনি ফর্মটি পূরণ করে এটি করতে পারেন

একটি কোড প্রকল্পে যোগদান করুন! (Join a Code Project!)

আমাদের সম্প্রদায়ে অবদান রাখার সর্বোত্তম উপায় হ'ল কোড অবদানকারী হয়ে উঠুন! আপনি যদি "ওয়ান অফ" মিনি-প্রকল্পগুলি শুরু করতে চান আপনি পাই সাইফ্ট গিটহাব ইস্যু পৃষ্ঠাতে এবং চিহ্নিত বিষয়গুলির জন্য অনুসন্ধান করতে পারেন Good First Issue.

দান করুন (Donate)

আপনার যদি আমাদের কোডবেসে অবদান রাখার সময় না থাকে তবে তবুও সমর্থন দিতে চান, আপনি আমাদের ওপেন কালেক্টিভেরও Backer হয়ে উঠতে পারেন। সমস্ত অনুদান আমাদের ওয়েব হোস্টিং এবং অন্যান্য সম্প্রদায় ব্যয় যেমন হ্যাকাথনস এবং মেটআপগুলির (hackathons and meetups!) দিকে যায়!


In [0]: